package com.managertrade.config;
import com.managertrade.dto.vo.OkxLeadTraderResponseVO;
import com.managertrade.util.strategy.XgbKlinePipeline;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Component
@Slf4j
public class OkxTraderCache {

    // 内存缓存：uniqueCode -> Trader
    private final ConcurrentHashMap<String, OkxLeadTraderResponseVO.Trader> traderCache = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, OkxLeadTraderResponseVO.Trader> tradeInfo = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Integer> traderCurrentPage = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        loadCacheFromCsv();
    }

    @PreDestroy
    public void destroy() {
        saveCacheToCsv();
    }
    /**
     * 批量获取实时仓位缓存
     * @param traders
     */

    public ConcurrentHashMap<String, OkxLeadTraderResponseVO.Trader> getAllTradeInfo() {
        return traderCache;
    }

    public Map<String,OkxLeadTraderResponseVO.Trader> getBatchTradeInfo(List<String> tradersCode) {
        Map<String,OkxLeadTraderResponseVO.Trader> queryResult =  new HashMap<>();
        for(String code :tradersCode){
            OkxLeadTraderResponseVO.Trader trader = tradeInfo.get(code);
            if(trader!=null){
                queryResult.put(code,trader);
            }
        }
        log.info("批量获取实时仓位缓存数量{}",queryResult.size());
        return queryResult;
    }
    /**
     * 批量获取缓存
     * @param traders
     */

    public Map<String,OkxLeadTraderResponseVO.Trader> getBatchCache(List<String> tradersCode) {
        Map<String,OkxLeadTraderResponseVO.Trader> queryResult =  new HashMap<>();
        for(String code :tradersCode){
            OkxLeadTraderResponseVO.Trader trader = traderCache.get(code);
            if(trader!=null){
                queryResult.put(code,trader);
            }
        }
        log.info("批量获取缓存交易者信息数量{}",queryResult.size());
        return queryResult;
    }
    public void updatetradeInfo(List<OkxLeadTraderResponseVO.Trader> traders) {
        if (traders != null) {
            traders.forEach(t -> tradeInfo.put(t.getUniqueCode(), t));
        }
        log.info("缓存交易者仓位信息数量{}",tradeInfo.keySet().size());
    }
    /**
     * 更新缓存
     * @param traders OKX 返回的交易员列表
     */
    public void updateCache(List<OkxLeadTraderResponseVO.Trader> traders) {
        if (traders != null) {
            traders.forEach(t -> traderCache.put(t.getUniqueCode(), t));
        }
        log.info("缓存交易者信息数量{}",traderCache.keySet().size());
    }
    public void updateTraderCurrentPage(Integer size) {
        traderCurrentPage.put("traderCurrentPage",size);
    }
    public Integer getTraderCurrentPage() {
        Integer traderCurrentPage1 = traderCurrentPage.get("traderCurrentPage");
        if (traderCurrentPage1 == null){
            traderCurrentPage1 = 1;
            traderCurrentPage.put("traderCurrentPage",traderCurrentPage1);
        }
        return traderCurrentPage1;
    }

    /**
     * 获取分页数据
     * @param page 页码，从1开始
     * @param pageSize 每页条数
     * @param nickNameFilter 昵称模糊筛选
     * @param minLeadDays 最小带单天数
     * @param state 跟单空位状态
     * @param minAssets 最小资产
     */
    public List<OkxLeadTraderResponseVO.Trader> getPage(int page, int pageSize,
                                                        String nickNameFilter,
                                                        Integer minLeadDays,
                                                        Integer state,
                                                        Double minAssets,
                                                        String sortType) {
        List<OkxLeadTraderResponseVO.Trader> all = new ArrayList<>(traderCache.values());
        // 条件筛选
        List<OkxLeadTraderResponseVO.Trader> filtered = all.stream()
                .filter(t -> nickNameFilter == null || t.getNickName().contains(nickNameFilter))
                .filter(t -> minLeadDays == null || parseInt(t.getLeadDays()) >= minLeadDays)
                .filter(t -> state == null || parseInt(t.getCopyState()) == state)
                .filter(t -> minAssets == null || parseDouble(t.getAum()) >= minAssets)
                .collect(Collectors.toList());

        // 排序
        if (sortType != null) {
            switch (sortType) {
                case "pnl": // 收益额
                    filtered.sort(Comparator.comparingDouble(t -> -parseDouble(t.getPnl())));
                    break;
                case "win_ratio": // 胜率
                    filtered.sort(Comparator.comparingDouble(t -> -parseDouble(t.getWinRatio())));
                    break;
                case "pnl_ratio": // 收益率
                    filtered.sort(Comparator.comparingDouble(t -> -parseDouble(t.getPnlRatio())));
                    break;
                case "aum": // 管理规模
                    filtered.sort(Comparator.comparingDouble(t -> -parseDouble(t.getAum())));
                    break;
                default: // 默认不排序
                    break;
            }
        }

        // 分页
        int start = (page - 1) * pageSize;
        int end = Math.min(start + pageSize, filtered.size());
        if (start >= filtered.size()) return new ArrayList<>();
        return filtered.subList(start, end);
    }

    /**
     * 获取总数
     */
    public int getTotal(String nickNameFilter,
                        Integer minLeadDays,
                        Integer state,
                        Double minAssets) {
        List<OkxLeadTraderResponseVO.Trader> all = new ArrayList<>(traderCache.values());
        return (int) all.stream()
                .filter(t -> nickNameFilter == null || t.getNickName().contains(nickNameFilter))
                .filter(t -> minLeadDays == null || parseInt(t.getLeadDays()) >= minLeadDays)
                .filter(t -> state == null || parseInt(t.getCopyState()) == state)
                .filter(t -> minAssets == null || parseDouble(t.getAum()) >= minAssets)
                .count();
    }

    // 安全转换工具
    private int parseInt(String s) {
        try { return Integer.parseInt(s); } catch (Exception e) { return 0; }
    }

    private double parseDouble(String s) {
        try { return Double.parseDouble(s); } catch (Exception e) { return 0; }
    }

    public int getTotalCount(String nickNameFilter,
                             Integer minLeadDays,
                             Integer state,
                             Double minAssets) {
        return (int) traderCache.values().stream()
                .filter(t -> nickNameFilter == null || t.getNickName().contains(nickNameFilter))
                .filter(t -> minLeadDays == null || parseInt(t.getLeadDays()) >= minLeadDays)
                .filter(t -> state == null || parseInt(t.getCopyState()) == state)
                .filter(t -> minAssets == null || parseDouble(t.getAum()) >= minAssets)
                .count();
    }
    public void saveCacheToCsv() {
        File file = new File(XgbKlinePipeline.DATA_DIR + "/traderCache.csv");
        try (BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {

            // 写入表头
            writer.write("uniqueCode,portLink,copyState,nickName,winRatio,pnl,pnlRatio,leadDays,copyTraderNum,insts\n");

            for (OkxLeadTraderResponseVO.Trader trader : traderCache.values()) {
                String insts = String.join("|", trader.getTraderInsts() != null ? trader.getTraderInsts() : new ArrayList<>());
                String line = String.format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
                        trader.getUniqueCode(),
                        trader.getPortLink(),
                        trader.getCopyState(),
                        trader.getNickName(),
                        trader.getWinRatio(),
                        trader.getPnl(),
                        trader.getPnlRatio(),
                        trader.getLeadDays(),
                        trader.getCopyTraderNum(),
                        insts);
                writer.write(line);
                writer.newLine();
            }
            writer.flush();
            log.info("缓存已持久化到 CSV，条数: {}", traderCache.size());
        } catch (IOException e) {
            log.error("缓存写入 CSV 失败", e);
        }
    }

    public void loadCacheFromCsv() {
        File file = new File(XgbKlinePipeline.DATA_DIR + "/traderCache.csv");
        if (!file.exists()) return;

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {

            String line;
            reader.readLine(); // 跳过表头
            Integer count = 1;
            while ((line = reader.readLine()) != null) {
                String[] arr = line.split(",", -1); // 防止空字段丢失
                if (arr.length < 8) continue;

                OkxLeadTraderResponseVO.Trader trader = new OkxLeadTraderResponseVO.Trader();
                trader.setUniqueCode(arr[0]);
                trader.setPortLink(arr[1]);
                trader.setCopyState(arr[2]);
                trader.setNickName(arr[3]);
                trader.setWinRatio(arr[4]);
                trader.setPnl(arr[5]);
                trader.setPnlRatio(arr[6]);
                trader.setLeadDays(arr[7]);
                trader.setCopyTraderNum(arr[8]);
                trader.setTraderInsts(Arrays.asList(arr[9].split("\\|")));

                traderCache.put(trader.getUniqueCode(), trader);
                count++;
            }
            traderCurrentPage.put("traderCurrentPage",(int) Math.floor(count-1 / 20));
            log.info("CSV 加载完成，缓存交易者数量: {}", traderCache.size());
        } catch (IOException e) {
            log.error("加载 CSV 失败", e);
        }

    }
}

